package de.koller.aframe.util.noise.n2D;

import de.koller.aframe.util.math.Function2DPreview;
import de.koller.aframe.util.math.IFunction2D;
import de.koller.aframe.util.misc.BufferedRandomFloat;
import de.koller.oceanFramework.OceanException;
import java.util.Random;

public class CPerlin2D extends Noise2D {

	public static boolean PRINT_PROGRESS = false;
	
	public final int ox, oy;
	private final BufferedRandomFloat crnd;
	private float persistence;
	private int steps;
	private int startStep;
	
	private int type = 0;
	
	public CPerlin2D( int ox, int oy, BufferedRandomFloat crnd, int startStep, int steps, float persistence ) {
		super( Perlin2D.noiseArray( startStep+steps ) );
		
		this.ox = ox;
		this.oy = oy;
		this.crnd = crnd;
		
		this.persistence = persistence;
		this.steps = steps;
		this.startStep = startStep;
		
		generate();
	}
	
	private CPerlin2D generate() {		
		for( int i = startStep; i < steps+startStep; i++ )
			handleStep( i );
		
		return this;
	}

	public CPerlin2D setType( int type ) {
		this.type = type;
		return this;
	}
	
	private void handleStep( int step ) {
		float amplitude = (float)Math.pow( persistence, step );
		int frequence = (int)Math.pow( 2, step );
		
		Noise2D n = new Noise2D( new float[ frequence + 3 ][ frequence + 3 ] );
		for( int ax = 0; ax < n.noise.length; ax++ ) {
			for( int ay = 0; ay < n.noise.length; ay++ ) {
				n.noise[ax][ay] = crnd.get(
//						(ox*frequence) + ax - 1,
//						(oy*frequence) + ay - 1,
						//frequence*ox - 1 + ax,
						//frequence*oy - 1 + ay,
						(int)(ox*3.5f)+ax,
						oy+ay,
						step,
						type );
			}
		}
		
		for( int ax = 0; ax < noise.length; ax++ ) {
			for( int ay = 0; ay < noise.length; ay++ ) {
				float x = 1f / (float)noise.length * ax;
				float y = 1f / (float)noise.length * ay;
				
				noise[ax][ay] += n.getValue( x, y ) * amplitude;
			}
			if( PRINT_PROGRESS && ax % 100 == 0 ) {
				String progress = "berechnen: " + step + " / " + (steps+startStep) + 
						" size: " + ax + " / " + noise.length;
				System.out.println( progress );
			}
		}
	}

	@Override
	public void normalize( float height ) {
		super.normalize( height );
		
		throw new OceanException();
	}
	
	private static int m = 0;
	public static void main( String[] args ) {
		while( true ) {
			BufferedRandomFloat bfr = new BufferedRandomFloat( "./temp/cNoiseTest", new Random() );
			CPerlin2D[] p = new CPerlin2D[4];

			for( int i = 0; i < 4; i++ ) {
				p[i] = new CPerlin2D( 
						i == 0 || i == 2 ? 0 : 1, 
						i == 0 || i == 1 ? 0 : 1, 
						bfr, 2, 1, .7f );
				p[i].normalizeMaxBound( Perlin2D.maxHeight( 2, 1, .7f ) );
				System.out.println( "P: " + p[i].ox + " / " + p[i].oy );
			}

			Function2DPreview.showFunction( 0, 0, 2, 2, new Matchfunction( p ) );
			
			m++;
		}
	}
	
	private static class Matchfunction implements IFunction2D {

		private final CPerlin2D[] perlin;
		
		public Matchfunction( CPerlin2D[] perlin ) {
			this.perlin = perlin;
		}
		
		@Override
		public float getValue( float x, float y ) {
			if( x >= 1 && y >= 1 ) return perlin[3].getValue( x-1f, y-1f );
			if( x >= 1 && y  < 1 ) return perlin[1].getValue( x-1f, y    );
			if( x <  1 && y >= 1 ) return perlin[2].getValue( x   , y-1f );
			if( x <  1 && y  < 1 ) return perlin[0].getValue( x   , y    );
			throw new OceanException( x + " " + y );
		}
		
	}
}
